<?php
/**
 * Hols class Image_BlobFinder
 *
 * @author daantimmer
 *
 * @package Library
 * @subpackage Image
 */

/**
 * This class is used to find blobs of colors in a run-length encoded image
 *
 * @author daantimmer
 */
class Image_BlobFinder{
	
	/**
	 * @var ArrayList
	 */
	private $bloblist;
	
	/**
	 * @var ArrayList
	 */
	private $templist;
	
	/**
	 * @var ArrayList
	 */
	private $rle;
	
	/**
	 * @var integer
	 */
	private $count;
	
	/**
	 * @var Image_Blob
	 */
	private $currObj;
	
	/**
	 * Finds blobs in $rle
	 *
	 * Every 4the line-scan this function will cleanup the found bloblist.
	 * It will combine blobs that are overlapping.
	 * At the end it will check minimum and maximum sizes of found blobs.
	 * (a blob has to be atleast 5x5 pixels large and can not be larger then 0.65*(width*height))
	 *
	 * @param integer $width	width of the image
	 * @param integer $height	height of the image
	 * @param ArrayList $rle	run-length encoded image
	 * @return ArrayList	ArrayList with found blobs
	 */
	public function findBlobs($width,$height,ArrayList $rle){
		$this->bloblist = new ArrayList();
		$this->templist = new ArrayList();
		$size = $width*$height;
		
		$this->rle = clone $rle;
		
		$this->count = 0;
		
		$index = null;
		
		for($y=0,$yn=$height,$q=1;$y<$yn;$y++,$q = ($q+1) & 0x03){
			for($x=0,$xn=$width;$x<$xn;){
				$index = $this->getNext($x,$width);
				
				if($index->getIndex() == Image_ColorIndex::instance()->WHITE
					|| $index->getIndex() == Image_ColorIndex::instance()->NONE
					|| $index->getIndex() == Image_ColorIndex::instance()->SKIN
//					|| $index->getIndex() == Image_ColorIndex::instance()->BLACK
				){
					$i = Image_ColorIndex::instance()->getKey($index->getIndex());
//					echo "bad color found...{$i}<br />\n";
				}else{
					$this->findConnectness($x,$x+$index->length(),$y,$index->getIndex(),$index->color());
				}
				
				$x += $index->length();
			}
			
			if($q == 0)
				$this->templist = $this->combineBlobs($this->templist,$size);
		}
		
		$this->templist = $this->combineBlobs($this->templist,$size);
		
		foreach($this->templist AS $blob){
			$sizeb = ($blob->getXEnd() - $blob->getXStart())
				* ($blob->getYEnd() - $blob->getYStart());
			
			if(($blob->getXEnd() - $blob->getXStart()) < 5
			|| ($blob->getYEnd() - $blob->getYStart()) < 5){
//				echo "Object too small <br />\n";
			}elseif($sizeb > ($size*0.65)){
				$s = $sizeb/$size;
				$c = Image_ColorIndex::instance()->getKey($blob->getIndex());
				echo "\tObject too large ({$s}) ({$c}) <br />\n";
//			}elseif(false){
			}else{
				$this->bloblist->add($blob);
			}
		}
		
		return $this->bloblist;
	}
	
	/**
	 * Combines blobs by checking if they are connected.
	 *
	 * Iterates over $blobList and creates a new ArrayList by expanding/adding blobs to this list
	 *
	 * @param ArrayList $blobList
	 * @param integer $size
	 * @return ArrayList new list containing Image_Blob's
	 */
	private function combineBlobs(ArrayList $blobList, $size){
		$blobListNew = new ArrayList();
		$blobcon = null;
		foreach($blobList AS $blob){
			if(($blobcon = $this->isBlobConnected($blob,$blobListNew)) != null){
				$blobcon->expand($blob);
			}else{
				$blobListNew->add($blob);
			}
		}
		
		return $blobListNew;
	}
	
	/**
	 * Returns next Run-length encoded color which can not exceed current width of the image.
	 *
	 * @param integer $x		start of next colour
	 * @param integer $width	max width of the image
	 * @return Image_RLEObj
	 */
	public function getNext($x, $width){
		if($this->rle->isEmpty() && $this->count == 0)
			return null;
		if($this->currObj == null || $this->count == 0){
			$this->rle->remove($this->currObj = $this->rle->getFirst());
			$this->count = $this->currObj->length();
		}
		
		$mx = $this->count;
		if($x + $this->count > $width)
			$mx = $width - $x;
		$this->count -= $mx;
		
		return new Image_RLEObj($this->currObj->color(), $this->currObj->getIndex(), $mx);
	}
	
	/**
	 * Checks if a blob is connected to a run-length encoded line
	 *
	 * @param integer $start	start of the RLE
	 * @param integer $end		end of the RLE
	 * @param integer $y		height of the current line
	 * @param integer $index	Image_Colorindex
	 * @param Image_Lib_Color $color	color to draw
	 * @return boolean
	 */
	public function findConnectness($start,$end,$y,$index,Image_Lib_Color $color){
		$found = false;
		foreach($this->templist AS $blob){
			if($blob->getIndex() == $index && $blob->touch($start,$end,$y)){
				$found = true;
				
				$blob->setXStart(min($start,$blob->getXStart()));
				$blob->setXEnd(max($end,$blob->getXEnd()));
				$blob->setYEnd($y);
				
				break;
			}
		}
		
		if($found == false){
			$this->templist->add(new Image_Blob($start,$end,$y,$index,$color));
		}
		
		return $found;
	}
	
	/**
	 * Checks if $blob is connected to any of the $bloblist
	 *
	 * @param Image_Blob $blob
	 * @param ArrayList $bloblist
	 * @return Image_Blob the blob that is matches in $bloblist or null
	 */
	public function isBlobConnected(Image_Blob $blob, ArrayList $bloblist){
		foreach($bloblist AS $blobcon){
			if($blobcon->getIndex() == $blob->getIndex()
				&& $blobcon->touchBlob($blob))
				return $blobcon;
		}
		return null;
	}
	
	/**
	 * @param ArrayList $list
	 * @param Image_Image $img
	 * @return Image_Image
	 */
	public function drawBlobs(ArrayList $list, Image_Image $img){
		foreach($list AS $blob){
			$bounds = $blob->get();
			
			$i = Image_ColorIndex::instance()->getKey($blob->getIndex());
			
//			echo "Color:...{$i} ... {$blob->getColor()}<br />\n";
			
//			if($blob->getIndex() == Image_ColorIndex::instance()->WHITE
//				|| $blob->getIndex() == Image_ColorIndex::instance()->NONE
//				|| $blob->getIndex() == Image_ColorIndex::instance()->SKIN){
//				echo "bad color found...{$blob->getIndex()}<br />\n";
//			}
			
			$this->drawHorz($img, $bounds[0][0], $bounds[1][0], $bounds[0][1], $blob->getColor());
			$this->drawHorz($img, $bounds[0][0], $bounds[1][0], $bounds[1][1], $blob->getColor());

			$this->drawVert($img, $bounds[0][1], $bounds[1][1], $bounds[0][0], $blob->getColor());
			$this->drawVert($img, $bounds[0][1], $bounds[1][1], $bounds[1][0], $blob->getColor());
		}
		
		return $img;
	}
	
	/**
	 * @param Image_Image $img
	 * @param integer $start
	 * @param integer $end
	 * @param integer $y
	 * @param Image_Lib_Color $color
	 * @return void
	 */
	private function drawHorz(Image_Image $img, $start,$end,$y, Image_Lib_Color $color){
		for($x=$start;$x<$end;$x++)
			$img->setColor($x,$y,$color);
	}
	
	/**
	 * @param Image_Image $img
	 * @param integer $start
	 * @param integer $end
	 * @param integer $x
	 * @param Image_Lib_Color $color
	 * @return void
	 */
	private function drawVert(Image_Image $img, $start,$end,$x, Image_Lib_Color $color){
		for($y=$start;$y<$end;$y++)
			$img->setColor($x,$y,$color);
	}
}